#include "kwdb/client.h"
#include "client_impl.h"
#include "transport.h"
#include "security_util.h"
#include <grpcpp/grpcpp.h>

namespace kwdb {

// ============================================================================
// KwdbClientImpl Implementation
// ============================================================================

KwdbClientImpl::KwdbClientImpl(const IngestConfig& config)
    : config_(config) {
    // Auto-detect local mode for ultra-low latency
    std::string endpoint = config_.ingest_endpoint;

    if (endpoint == "local" || endpoint.empty() || endpoint == "localhost") {
        // Use Unix Domain Socket for local connections (microsecond latency!)
        const char* uds_path_env = getenv("KWDB_UDS_PATH");
        std::string uds_path = uds_path_env ? uds_path_env : "/tmp/kwdb-ingestd.sock";
        endpoint = "unix://" + uds_path;
        std::cout << "[SDK] Using UDS local mode for ultra-low latency at " << uds_path << std::endl;
    } else if (endpoint.find("://") == std::string::npos) {
        // Add default scheme if missing
        endpoint = "dns:///" + endpoint;
    }

    // Create gRPC channel with optimizations
    grpc::ChannelArguments args;
    args.SetMaxReceiveMessageSize(100 * 1024 * 1024);  // 100MB
    args.SetMaxSendMessageSize(100 * 1024 * 1024);     // 100MB

    // Create channel credentials based on security configuration
    auto credentials = security::create_channel_credentials(config_.security);

    channel_ = grpc::CreateCustomChannel(
        endpoint,
        credentials,
        args
    );

    // Create stubs
    stub_ = kwdb::ingest::v1::IngestService::NewStub(channel_);
    query_stub_ = kwdb::ingest::v1::QueryService::NewStub(channel_);

    // Log connection type for debugging
    if (endpoint.find("unix://") == 0) {
        std::cout << "[SDK] Connected via Unix Domain Socket (fastest)" << std::endl;
    } else {
        std::string security_info = config_.security.tls ? " (TLS enabled)" : "";
        std::cout << "[SDK] Connected to " << endpoint << security_info << std::endl;
    }

    if (config_.security.has_auth()) {
        std::cout << "[SDK] Authentication configured" << std::endl;
    }
}

Status KwdbClientImpl::write(const Point& point) {
    // Delegate to batch_write
    return batch_write({point});
}

Status KwdbClientImpl::batch_write(const std::vector<Point>& points) {
    // Build request
    kwdb::ingest::v1::WriteRequest request;
    for (const auto& point : points) {
        auto* proto_point = request.add_points();
        transport::convert_point(point, proto_point);
    }

    // Set up context with deadline
    grpc::ClientContext context;
    auto deadline = std::chrono::system_clock::now() +
                    std::chrono::milliseconds(config_.timeout_ms);
    context.set_deadline(deadline);

    // Add authentication metadata if configured
    security::add_auth_metadata(context, config_.security);

    // Call RPC
    kwdb::ingest::v1::WriteResponse response;
    grpc::Status grpc_status = stub_->WriteBatch(&context, request, &response);

    // Check gRPC-level errors first
    if (!grpc_status.ok()) {
        return transport::grpc_status_to_sdk_status(grpc_status);
    }

    // Check server-side response
    if (response.code() != 0) {
        return Status(response.code(), response.message());
    }

    return Status(0, "OK");
}

Status KwdbClientImpl::query(const std::string& sql, std::vector<std::vector<std::string>>& rows) {
    // Build request
    kwdb::ingest::v1::QueryReq request;
    request.set_sql(sql);
    request.set_fetch_size(0); // 0 means fetch all

    // Set up context with deadline
    grpc::ClientContext context;
    auto deadline = std::chrono::system_clock::now() +
                    std::chrono::milliseconds(config_.timeout_ms);
    context.set_deadline(deadline);

    // Add authentication metadata if configured
    security::add_auth_metadata(context, config_.security);

    // Call RPC
    kwdb::ingest::v1::QueryResp response;
    grpc::Status grpc_status = query_stub_->Query(&context, request, &response);

    // Check gRPC-level errors first
    if (!grpc_status.ok()) {
        return transport::grpc_status_to_sdk_status(grpc_status);
    }

    // Check server-side response
    if (response.code() != 0) {
        return Status(response.code(), response.message());
    }

    // Extract rows
    rows.clear();
    rows.reserve(response.rows_size());
    for (const auto& row : response.rows()) {
        std::vector<std::string> row_values;
        row_values.reserve(row.values_size());
        for (const auto& value : row.values()) {
            row_values.push_back(value);
        }
        rows.push_back(std::move(row_values));
    }

    return Status(0, "OK");
}

// ============================================================================
// KwdbClient Public API Implementation
// ============================================================================

KwdbClient::KwdbClient(const IngestConfig& config)
    : impl_(std::make_unique<KwdbClientImpl>(config)) {}

KwdbClient::~KwdbClient() = default;

KwdbClient::KwdbClient(KwdbClient&&) noexcept = default;
KwdbClient& KwdbClient::operator=(KwdbClient&&) noexcept = default;

Status KwdbClient::write(const Point& point) {
    return impl_->write(point);
}

Status KwdbClient::batch_write(const std::vector<Point>& points) {
    return impl_->batch_write(points);
}

Status KwdbClient::query(const std::string& sql, std::vector<std::vector<std::string>>& rows) {
    return impl_->query(sql, rows);
}

} // namespace kwdb